package com.rajaev.dtu.tractor.domain.service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.rajaev.dtu.tractor.app.service.UserService;
import com.rajaev.dtu.tractor.domain.entity.UserInfo;
import com.rajaev.dtu.tractor.domain.repository.mysql.ResourceDao;
import com.rajaev.dtu.tractor.domain.repository.mysql.UserCustomerDao;
import com.rajaev.dtu.tractor.domain.repository.mysql.UserDao;
import com.rajaev.dtu.tractor.domain.repository.mysql.UserRoleDao;
import com.rajaev.dtu.tractor.domain.vo.ResourceVo;
import com.rajaev.dtu.tractor.domain.vo.UserVo;
import com.rajaev.dtu.tractor.infrastructure.base.errors.BusinessException;
import com.rajaev.dtu.tractor.infrastructure.base.errors.ErrorCodes;
import com.rajaev.dtu.tractor.interfaces.dto.LoginResponseDto;
import org.dozer.Mapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import static com.rajaev.dtu.tractor.infrastructure.base.common.Constants.cacheTokenPrefix;
import static com.rajaev.dtu.tractor.infrastructure.helper.MD5ValueGenerator.generateValue;

@Service
public class UserInfoDomain implements UserService {

    @Autowired
    UserDao userDao;

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    UserRoleDao userRoleDao;

    @Autowired
    UserCustomerDao userCustomerDao;

    @Autowired
    ResourceDao resourceDao;

    @Autowired
    UserInfoDomain userInfoDomain;

    @Autowired
    protected Mapper dozerMapper;


    private static final String defaultPassword = "123456";


    @Override
    public UserInfo getUserByLoginName(String loginName) throws BusinessException {
        UserInfo userPo = userDao.selectUserByLoginName(loginName);
        if (userPo == null) {
            throw new BusinessException(ErrorCodes.USER_NOT_EXISTS);
        }
        return userPo;
    }

    @Override
    public UserInfo queryUserInfo(String loginName) {
        return userDao.selectUserByLoginName(loginName);
    }

    @Override
    @Transactional(isolation = Isolation.READ_UNCOMMITTED, rollbackFor = Exception.class)
    @CacheEvict(cacheNames = "users", allEntries = true)
    public void insertUser(UserVo userVo) throws BusinessException {
        if (userDao.selectUserByLoginName(userVo.getLoginName()) != null) {
            throw new BusinessException(ErrorCodes.ERR_USER_HAS_EXIST);
        }

        UserInfo userInfo = dozerMapper.map(userVo, UserInfo.class);
        userInfo.setPassword(generateValue(defaultPassword));
        userInfo.setCreateDatetime(new Date());

        //新增用户的角色信息
        userDao.insert(userInfo);
        if (userVo.getRoleIds() != null && userVo.getRoleIds().size() > 0) {
            userRoleDao.insert(userVo.getRoleIds(), userInfo.getId());
        }

        //新增用户的客户信息
        userCustomerDao.delete(userVo.getId());
        if (userVo.getCustomerIds() != null && userVo.getCustomerIds().size() > 0) {
            userCustomerDao.insert(userVo.getCustomerIds(), userInfo.getId());
        }

    }


    @Override
    public void resetPassword(Integer userId) {
        userDao.updateUserPassword(userId, generateValue(defaultPassword));
    }

    @Override
    public List<Integer> selectUserCustomerIds(Integer userId) {
        return userDao.selectUserCustomerIds(userId);

    }

    @Override
    @CacheEvict(cacheNames = "users", allEntries = true)
    public void deleteUser(Integer userId) {
        userDao.deleteUser(userId);
    }


    @Override
    @Transactional(isolation = Isolation.READ_UNCOMMITTED, rollbackFor = Exception.class)
    public void updatePassword(Integer userId, String newPassword, String oldPassword) throws BusinessException {
        UserInfo userInfo = userDao.selectUserById(userId);
        if (!userInfo.getPassword().equalsIgnoreCase(generateValue(oldPassword))) {
            throw new BusinessException(ErrorCodes.ERR_OLD_SYSTEM);
        }
        userDao.updateUserPassword(userId, generateValue(newPassword));
    }

    @Override
    public void updateOriginalPassword(String newPassword, UserInfo userInfo) throws BusinessException {
        if (!userInfo.getPassword().equalsIgnoreCase(generateValue(defaultPassword))) {
            throw new BusinessException(ErrorCodes.ERR_PASSWORD);
        }
        if (newPassword.equalsIgnoreCase(defaultPassword)) {
            throw new BusinessException(ErrorCodes.ERR_NEWPASSWORD_CANNOT_SAME_WITH_DEFAULT);
        }
        userDao.updateUserPassword(userInfo.getId(), generateValue(newPassword));
    }

    @Override
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_UNCOMMITTED)
    @CacheEvict(allEntries = true, cacheNames = "users")
    public void editUser(UserVo userVo) throws BusinessException {
        UserInfo originUserpo = userDao.selectUserById(userVo.getId());

        //当登录名有修改时，需要判断新的登录名是否在表中已经存在
        if (!originUserpo.getLoginName().equalsIgnoreCase(userVo.getLoginName())) {
            if (userDao.selectUserByLoginName(userVo.getLoginName()) != null) {
                throw new BusinessException(ErrorCodes.ERR_USER_HAS_EXIST);
            }
        }

        UserInfo userPo = dozerMapper.map(userVo, UserInfo.class);
        //更新用户的角色信息
        userRoleDao.deleteUserRoles(userVo.getId());
        if (userVo.getRoleIds() != null && userVo.getRoleIds().size() > 0) {
            userRoleDao.insert(userVo.getRoleIds(), userVo.getId());
        }

        //更新用户的客户信息
        userCustomerDao.delete(userVo.getId());
        if (userVo.getCustomerIds() != null && userVo.getCustomerIds().size() > 0) {
            userCustomerDao.insert(userVo.getCustomerIds(), userVo.getId());

        }

        //更新用户的基本信息
        userDao.update(userPo);
    }

    @Override
    public void updateUserSystemDisplayName(Integer userId,String systemDisplayName) {
        userDao.updateUserSystemDisplayName(userId, systemDisplayName);
    }

    @Override
    public PageInfo<UserVo> pageQueryUsers(Integer pageSize, Integer pageNum) {
        PageInfo<UserVo> page = userInfoDomain.pageQueryUserPos(pageSize, pageNum);
        page.getList().forEach(userVo -> {
            List<Integer> roleIds = new ArrayList<>();
            List<Integer> customerIds = new ArrayList<>();
            userVo.getRoles().forEach(roleVo -> roleIds.add(roleVo.getId()));
            userVo.getCustomerVos().forEach(customerVo -> customerIds.add(customerVo.getId()));
            userVo.setCustomerIds(customerIds);
            userVo.setRoleIds(roleIds);
        });
        return page;
    }

    @Override
    public List<ResourceVo> selectResource(Integer userId) {
        return resourceDao.selectUserResource(userId);
    }

    public PageInfo<UserVo> pageQueryUserPos(Integer pageSize, Integer pageNum) {
        PageInfo<UserVo> page = PageHelper.startPage(pageNum, pageSize).doSelectPageInfo(() -> userDao.selectUsers());
        return page;
    }

    @Override
    public void logout(String token) {
        redisTemplate.delete(cacheTokenPrefix + token);
    }

    @Override
    public LoginResponseDto auth(String loginName, String password) throws BusinessException {

        UserInfo userInfo = getUserByLoginName(loginName);

        //校验用户的有效期
        checkValidDate(userInfo);

        if (!userInfo.getPassword().equalsIgnoreCase(generateValue(password))) {
            //密码不匹配
            throw new BusinessException(ErrorCodes.ERR_PASSWORD);
        }

        LoginResponseDto.LoginResponseDtoBuilder loginResponseDtoBuilder = LoginResponseDto.builder();

        loginResponseDtoBuilder.isNeedUpdatePassword(checkUserPasswordIsDefault(userInfo)).
                mobile(userInfo.getMobile()).
                userName(userInfo.getUserName())
                .systemDisplayName(userInfo.getSystemDisplayName());

        //生成token
        String token = generateValue(UUID.randomUUID().toString());

        loginResponseDtoBuilder.token(token);

        cacheToken(userInfo, token);

        return loginResponseDtoBuilder.build();
    }


    /**
     * 将token存入缓存
     *  @param userInfo
     * @param token
     */
    private void cacheToken(@NotNull UserInfo userInfo, @NotNull String token) {
        redisTemplate.opsForValue().set(cacheTokenPrefix + token, userInfo.getLoginName(), 30, TimeUnit.MINUTES);
    }

    public void checkValidDate(UserInfo userInfo) throws BusinessException {
        if (userInfo.getValidDatetimeStart() != null
                && System.currentTimeMillis() - userInfo.getValidDatetimeStart().getTime() < 0) {
            throw new BusinessException(ErrorCodes.ERR_USER_NO_IN_USE);
        }
        if (userInfo.getValidDatetimeEnd() != null &&
                userInfo.getValidDatetimeEnd().getTime() - System.currentTimeMillis() < 0) {
            throw new BusinessException(ErrorCodes.ERR_USER_INVALID);
        }
    }


    private boolean checkUserPasswordIsDefault(UserInfo userInfo) {
        return userInfo.getPassword().equalsIgnoreCase(generateValue(defaultPassword));
    }
}